/*     */ package com.zimbra.cs.server;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import java.net.InetAddress;
/*     */ import java.net.UnknownHostException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collection;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.ConcurrentHashMap;
/*     */ import java.util.concurrent.ConcurrentMap;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ServerThrottle
/*     */ {
/*  39 */   private static final ConcurrentMap<String, ServerThrottle> instances = new ConcurrentHashMap();
/*     */   
/*     */   public static ServerThrottle getThrottle(String serverType) {
/*  42 */     return (ServerThrottle)instances.get(serverType);
/*     */   }
/*     */   
/*     */   public static synchronized void configureThrottle(String serverType, int ipReqLimit, int acctReqLimit, Iterable<String> safeHosts, Iterable<String> whitelistHosts)
/*     */   {
/*  47 */     ServerThrottle throttle = getThrottle(serverType);
/*  48 */     if (throttle == null) {
/*  49 */       throttle = new ServerThrottle(serverType);
/*  50 */       instances.put(serverType, throttle);
/*  51 */       startReaper();
/*     */     }
/*  53 */     throttle.setIpReqsPerSecond(ipReqLimit);
/*  54 */     throttle.setAcctReqsPerSecond(acctReqLimit);
/*  55 */     for (String hostname : safeHosts) {
/*  56 */       throttle.addToHostList(hostname, false);
/*     */     }
/*  58 */     for (String hostname : whitelistHosts) {
/*  59 */       throttle.addToHostList(hostname, true);
/*     */     }
/*     */   }
/*     */   
/*  63 */   private static boolean reaping = false;
/*     */   private String serverType;
/*     */   
/*  66 */   private static synchronized void startReaper() { if (reaping) {
/*  67 */       return;
/*     */     }
/*  69 */     reaping = true;
/*     */     
/*  71 */     Thread t = new Thread("ServerThrottleReaper")
/*     */     {
/*     */       public void run() {
/*     */         try {
/*     */           for (;;) {
/*  76 */             for (Iterator i$ = ServerThrottle.instances.values().iterator(); i$.hasNext();) { throttle = (ServerThrottle)i$.next();
/*  77 */               for (Map.Entry<String, List<Long>> reqsEntry : throttle.ipReqs.entrySet()) {
/*  78 */                 List<Long> reqs = (List)reqsEntry.getValue();
/*  79 */                 synchronized (reqs) {
/*  80 */                   throttle.pruneStaleRequests(reqs);
/*  81 */                   if (reqs.size() == 0) {
/*  82 */                     throttle.ipReqs.remove(reqsEntry.getKey());
/*     */                   }
/*     */                 }
/*     */               }
/*  86 */               for (Map.Entry<String, List<Long>> reqsEntry : throttle.acctReqs.entrySet()) {
/*  87 */                 List<Long> reqs = (List)reqsEntry.getValue();
/*  88 */                 synchronized (reqs) {
/*  89 */                   throttle.pruneStaleRequests(reqs);
/*  90 */                   if (reqs.size() == 0)
/*  91 */                     throttle.acctReqs.remove(reqsEntry.getKey());
/*     */                 }
/*     */               }
/*     */             }
/*     */             try {
/*     */               ServerThrottle throttle;
/*  97 */               ZimbraLog.net.debug("zimbra_mailbox_throttle_reap_interval = %s", new Object[] { Long.valueOf(LC.zimbra_mailbox_throttle_reap_interval.longValue()) });
/*  98 */               Thread.sleep(LC.zimbra_mailbox_throttle_reap_interval.longValue());
/*     */             }
/*     */             catch (InterruptedException ie) {}
/*     */           }
/*     */         } catch (Throwable t) {
/* 103 */           ZimbraLog.net.error("caught throwable during throttle reaping.", t);
/*     */         }
/*     */         
/*     */       }
/* 107 */     };
/* 108 */     t.setDaemon(true);
/* 109 */     t.start();
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   ServerThrottle(String serverType) {
/* 114 */     this.serverType = serverType;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/* 119 */   private int ipReqsPerSecond = 0;
/*     */   
/* 121 */   private int acctReqsPerSecond = 0;
/*     */   
/* 123 */   private ConcurrentMap<String, List<Long>> ipReqs = new ConcurrentHashMap();
/*     */   
/*     */ 
/* 126 */   private ConcurrentMap<String, List<Long>> acctReqs = new ConcurrentHashMap();
/*     */   
/*     */ 
/* 129 */   private Set<String> ignoredIps = new HashSet();
/*     */   
/* 131 */   private Set<String> whitelistIps = new HashSet();
/*     */   
/*     */   @VisibleForTesting
/*     */   void setIpReqsPerSecond(int ipReqsPerSecond) {
/* 135 */     this.ipReqsPerSecond = ipReqsPerSecond;
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   void setAcctReqsPerSecond(int acctReqsPerSecond) {
/* 140 */     this.acctReqsPerSecond = acctReqsPerSecond;
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   void addIgnoredIp(String ip) {
/* 145 */     ZimbraLog.net.debug("adding IP %s to throttle ignore list", new Object[] { ip });
/* 146 */     this.ignoredIps.add(ip);
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   void addWhitelistIp(String ip) {
/* 151 */     ZimbraLog.net.debug("adding IP %s to throttle whitelist", new Object[] { ip });
/* 152 */     this.whitelistIps.add(ip);
/* 153 */     addIgnoredIp(ip);
/*     */   }
/*     */   
/*     */   private void addToHostList(String hostname, boolean whitelist) {
/*     */     try {
/* 158 */       InetAddress[] addrs = InetAddress.getAllByName(hostname);
/* 159 */       if (addrs != null) {
/* 160 */         for (InetAddress addr : addrs) {
/* 161 */           if (whitelist) {
/* 162 */             addWhitelistIp(addr.getHostAddress());
/*     */           } else {
/* 164 */             addIgnoredIp(addr.getHostAddress());
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     catch (UnknownHostException e) {
/* 170 */       ZimbraLog.net.warn("unknown host %s cannot be added to throttle %slist. %s requests from this host may be throttled. If this host is a proxy please add it to your DNS.", new Object[] { whitelist ? "white" : "ignore ", hostname, this.serverType });
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   public boolean isIpThrottled(String ip)
/*     */   {
/* 177 */     if (ip == null)
/* 178 */       return false;
/* 179 */     if (isIpInSet(ip, this.ignoredIps)) {
/* 180 */       return false;
/*     */     }
/* 182 */     return isThrottled(this.ipReqs, ip, this.ipReqsPerSecond);
/*     */   }
/*     */   
/*     */   public boolean isIpWhitelisted(String ip)
/*     */   {
/* 187 */     if (ip == null) {
/* 188 */       return false;
/*     */     }
/* 190 */     return isIpInSet(ip, this.whitelistIps);
/*     */   }
/*     */   
/*     */   public boolean isAccountThrottled(String acctId, String... requestIps)
/*     */   {
/* 195 */     for (String ip : requestIps) {
/* 196 */       if (isIpWhitelisted(ip)) {
/* 197 */         return false;
/*     */       }
/*     */     }
/* 200 */     return isThrottled(this.acctReqs, acctId, this.acctReqsPerSecond);
/*     */   }
/*     */   
/*     */   private boolean isIpInSet(String ip, Set<String> ips) {
/* 204 */     if (ip == null)
/* 205 */       return false;
/* 206 */     if (ips.contains(ip))
/* 207 */       return true;
/* 208 */     if ((ip.indexOf('%') >= 0) && (ips.contains(ip.substring(0, ip.indexOf('%')))))
/*     */     {
/*     */ 
/*     */ 
/* 212 */       return true;
/*     */     }
/* 214 */     return false;
/*     */   }
/*     */   
/*     */   private boolean isThrottled(Map<String, List<Long>> reqMap, String key, int limit)
/*     */   {
/* 219 */     if (limit <= 0) {
/* 220 */       return false;
/*     */     }
/* 222 */     List<Long> reqs = getReqs(key, reqMap);
/* 223 */     synchronized (reqs)
/*     */     {
/*     */ 
/* 226 */       reqs.add(Long.valueOf(System.currentTimeMillis()));
/* 227 */       if (reqs.size() > limit) {
/* 228 */         pruneStaleRequests(reqs);
/* 229 */         return reqs.size() > limit;
/*     */       }
/* 231 */       return false;
/*     */     }
/*     */   }
/*     */   
/*     */   private void pruneStaleRequests(List<Long> reqTimes)
/*     */   {
/* 237 */     long now = System.currentTimeMillis();
/* 238 */     Iterator<Long> it = reqTimes.iterator();
/* 239 */     while (it.hasNext()) {
/* 240 */       Long time = (Long)it.next();
/* 241 */       if (time.longValue() >= now - 1000L) break;
/* 242 */       it.remove();
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   private List<Long> getReqs(String key, Map<String, List<Long>> reqMap)
/*     */   {
/* 251 */     List<Long> reqs = (List)reqMap.get(key);
/* 252 */     if (reqs == null)
/*     */     {
/*     */ 
/*     */ 
/* 256 */       reqs = new ArrayList();
/* 257 */       reqMap.put(key, reqs);
/*     */     }
/* 259 */     return reqs;
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   void addIpReq(String ip, Long time) {
/* 264 */     addReq(ip, this.ipReqs, time);
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   void addAcctReq(String ip, Long time) {
/* 269 */     addReq(ip, this.acctReqs, time);
/*     */   }
/*     */   
/*     */   private void addReq(String key, Map<String, List<Long>> reqMap, Long time) {
/* 273 */     List<Long> reqs = getReqs(key, reqMap);
/* 274 */     synchronized (reqs) {
/* 275 */       reqs.add(time);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/server/ServerThrottle.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */